package com.zkh.myutils.page;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.zkh.myutils.utils.Assert;
import com.zkh.myutils.utils.StringUtils;

/**
 * 分页对象
 * @author zkh
 */
public class Pagination<T> {
	//总条数
	private long totalItem;
	//总页数
	private int totalPage;
	//当前页
	private int pageIndex;
	//每页条数
	private int pageItem;
	//数据
	private List<T> dataList;
	//分页信息
	private String pageInfo;
	
	public Pagination() {}
	
	/**
	 * 构造方法
	 * @param pageItem 每页数量
	 * @param pageIndex 当前页码
	 * @param totalItem 总记录数
	 */
	public Pagination(int pageItem, int pageIndex, long totalItem) {
		//基本属性
		this.pageItem = pageItem;
		this.pageIndex = pageIndex;
		this.totalItem = totalItem;
		//计算总页数
		this.totalPage = (int) (totalItem - 1) / pageItem + 1;
		//当前页码超出总页码数量
		if(this.pageIndex>this.totalPage) {
			this.pageIndex = this.totalPage;
		}
	}
	
	/**
	 * 构造方法。使用已有分页对象构造新的分页对象，不装载数据
	 * @param pagination 分页对象
	 */
	public <S> Pagination(Pagination<S> pagination){
		this(pagination, null);
	}
	
	/**
	 * 构造方法。使用已有分页对象构造新的分页对象
	 * @param pagination 分页对象
	 * @param dataConverter 自定义数据转换器，用来装载数据
	 */
	public <S> Pagination(Pagination<S> pagination, Function<S, T> dataConverter){
		this.totalItem = pagination.totalItem;
		this.totalPage = pagination.totalPage;
		this.pageItem = pagination.pageItem;
		this.pageIndex = pagination.pageIndex;
		//有数据转换器
		if(dataConverter!=null) {
			this.dataList = pagination.getDataList().stream().map(dataConverter).collect(Collectors.toList());
		}
	}
	
	/**
	 * 生成页码信息
	 * @param pageInfoAttr 生成的页码信息
	 */
	public void createPageInfo(PageInfoAttr pageInfoAttr) {
		//临时变量
		String queryString=pageInfoAttr.getQueryString(), linkHtml=pageInfoAttr.getLinkHtml(), noLinkHtml=pageInfoAttr.getNoLinkHtml();
		String[] prevNextTexts=pageInfoAttr.getPrevNextTexts(), beginEndTexts=pageInfoAttr.getBeginEndTexts(),outerHtml=pageInfoAttr.getOuterHtml();
		int pageNumber=pageInfoAttr.getPageNumber();
		//默认全部展示
		int start = 1, end = totalPage;
		//总页数大于展示页数
		if(totalPage>pageNumber) {
			//开始页码
			start = Math.max(pageIndex-pageNumber/2, 1);
			//结束页码
			end = start + pageNumber - 1;
			//检查边界。超过边界，则重新计算页码
			if(end>totalPage) {
				end = totalPage; start = end - pageNumber + 1;
			}
		}
		//最终内容容器
		List<String> leftResList = new ArrayList<>(), rightResList = new ArrayList<>();
		//外层内容
		if(outerHtml!=null) {
			Assert.doIfNotEmpty(outerHtml[0], leftResList::add);
			Assert.doIfNotEmpty(outerHtml[1], rightResList::add);
		}
		//首页和尾页
		if(beginEndTexts!=null) {
			//可跳转html
			String link = beginEndTexts.length>2 ? beginEndTexts[2] : linkHtml;
			//渲染内容
			Assert.doIfNotEmpty(beginEndTexts[0], beginText->leftResList.add(renderLink(queryString, link, null, 1, beginText)));
			Assert.doIfNotEmpty(beginEndTexts[1], endText->rightResList.add(0, renderLink(queryString, link, null, totalPage, endText)));
		}
		//上一页、下一页
		if(prevNextTexts!=null) {
			//可跳转html和不可跳转html
			String link = prevNextTexts.length>2 ? prevNextTexts[2] : linkHtml;
			String noLink = prevNextTexts.length>3 ? prevNextTexts[3] : noLinkHtml;
			//渲染内容
			Assert.doIfNotEmpty(prevNextTexts[0], prevText->leftResList.add(renderLink(queryString, link, noLink, Math.max(pageIndex-1, 1), prevText)));
			Assert.doIfNotEmpty(prevNextTexts[1], nextText->rightResList.add(0, renderLink(queryString, link, noLink, Math.min(pageIndex+1, totalPage), nextText)));
		}
		//遍历页码
		for(int i=start; i<=end; i++) {
			leftResList.add(renderLink(queryString, linkHtml, noLinkHtml, i, String.valueOf(i)));
		}
		//拼接右边内容
		leftResList.addAll(rightResList);
		//保存拼接内容
		this.pageInfo = String.join("", leftResList);
	}
	
	/**
	 * 渲染链接
	 * @param queryString 查询参数
	 * @param linkHtml 包含链接的html模板
	 * @param noLinkHtml 不包含链接的html模板
	 * @param currentPageIndex 当前页码
	 * @param showPageIndex 当前展示的页码文本
	 */
	private String renderLink(String queryString, String linkHtml, String noLinkHtml, int currentPageIndex, String showPageIndex) {
		//当前页，不使用链接
		if(noLinkHtml!=null && pageIndex==currentPageIndex) {
			return noLinkHtml.replace("{pageIndex}", showPageIndex);
		}
		//非当前页，使用链接
		return linkHtml.replace("{link}", "?" + formatQueryString(queryString, currentPageIndex)).replace("{pageIndex}", showPageIndex);
	}
	
	/**
	 * 格式化queryString
	 * @param queryString 查询参数
	 * @param pageIndex 要格式化的页码
	 * @return 格式化后的queryString
	 */
	private String formatQueryString(String queryString, int pageIndex) {
		//如果queryString为空，则直接返回页码值
		if(StringUtils.isEmpty(queryString)) {
			return "pageIndex=" + pageIndex;
		}
		//格式化后的列表
		List<String> paramList = new ArrayList<>();
		//页码标识
		boolean hasPageIndex = false;
		//以&为分隔符分割出所有请求数据
		//循环遍历请求数据，如果存在pageIndex，则更新页码信息
		for(String query: queryString.split("&")) {
			//当前含pageIndex
			boolean bool = query.startsWith("pageIndex=");
			//已包含pageIndex入参，则更新页码，否则直接保存
			paramList.add(bool ? "pageIndex="+pageIndex : query);
			//含页码标识
			hasPageIndex = hasPageIndex || bool;
		}
		//没有页码
		if(!hasPageIndex) {
			paramList.add("pageIndex="+pageIndex);
		}
		//返回结果
		return String.join("&", paramList);
	}
	
	/**
	 * 获取总条数
	 * @return
	 */
	public long getTotalItem() {
		return totalItem;
	}
	/**
	 * 设置总条数
	 * @param totalItem 数据总条数
	 */
	public void setTotalItem(long totalItem) {
		this.totalItem = totalItem;
	}
	/**
	 * 获取总页数
	 * @return
	 */
	public int getTotalPage() {
		return totalPage;
	}
	/**
	 * 设置总页数
	 * @param totalPage 总页数
	 */
	public void setTotalPage(int totalPage) {
		this.totalPage = totalPage;
	}
	/**
	 * 获取当前页码
	 * @return
	 */
	public int getPageIndex() {
		return pageIndex;
	}
	/**
	 * 设置当前页码
	 * @param pageIndex 当前页码
	 */
	public void setPageIndex(int pageIndex) {
		this.pageIndex = pageIndex;
	}
	/**
	 * 获取每页条数
	 * @return
	 */
	public int getPageItem() {
		return pageItem;
	}
	/**
	 * 设置每页条数
	 * @param pageItem 每页条数
	 */
	public void setPageItem(int pageItem) {
		this.pageItem = pageItem;
	}
	/**
	 * 获取分页数据
	 * @return
	 */
	public List<T> getDataList() {
		return dataList;
	}
	/**
	 * 设置分页数据
	 * @param dataList 分页数据
	 */
	public void setDataList(List<T> dataList) {
		this.dataList = dataList;
	}
	/**
	 * 获取分页信息
	 */
	public String getPageInfo() {
		return pageInfo;
	}
}
